// includes
#include "message.h"

// globals
PacketList *freeList;

// definitions
Packet* getPacket()
{
    Packet *packet;

    if (freeList->head == NULL)
    {
        packet = (Packet *)malloc(sizeof(Packet));
    }
    else
    {
        packet = freeList->head;
        freeList->head = packet->next;
        freeList->packetCount--;
    }

    return packet;
}

void returnPacket(Packet *packet)
{
    if (packet == NULL)
        return;

    // clean packet for reuse
    packet->from = 0;
    packet->to = 0;
    packet->next = NULL;

    packet->next = freeList->head;
    freeList->head = packet;
    freeList->packetCount++;
}

PacketList* createPacketList()
{
    PacketList *packetList = (PacketList *)malloc(sizeof(PacketList));
    packetList->packetCount = 0;
    packetList->head = NULL;

    return packetList;
}

void destroyPacketList(PacketList *packetList)
{
    Packet *packet = NULL, *nextPacket = NULL;

    if (packetList != NULL)
    {
        for (packet = packetList->head; packet != NULL; packet = nextPacket)
        {
            nextPacket = packet->next;
            returnPacket(packet);
        }

        free(packetList);
    }
}

void msgInitialize()
{
    freeList = createPacketList();
}

void msgTerminate()
{
    Packet *packet = NULL, *nextPacket = NULL;

    if (freeList != NULL)
    {
        for (packet = freeList->head; packet != NULL; packet = nextPacket)
        {
            nextPacket = packet->next;
            free(packet);
        }

        free(freeList);
    }
}

void pushPacket(PacketList *list, Packet *packet)
{
    if ((packet == NULL) || (list == NULL))
        return;

    packet->next = list->head;
    list->head = packet;

    list->packetCount++;
}

Packet* popPacket(PacketList *list)
{
    Packet *packet;
    if (list == NULL)
        return NULL;

    packet = list->head;

    if (packet != NULL)
    {
        list->head = packet->next;
        list->packetCount--;
    }

    return packet;
}

bool recvMessage(PacketList *inbox, Message *msg, int *from)
{
    Packet *packet;

    if ((inbox == NULL) || (msg == NULL))
        return false;

    if ((packet = popPacket(inbox)) != NULL)
    {
        // copy data
        *from = packet->from;
        MESSAGE_COPY (msg, &(packet->payload))

        returnPacket(packet);

        return true;
    }

    return false;
}

bool sendMessage(PacketList *outbox, Message *msg, int from, int to)
{
    Packet *packet;

    if ((outbox == NULL) || (msg == NULL))
        return false;

    packet = getPacket();
    MESSAGE_COPY(&(packet->payload), msg)

    packet->from = from;
    packet->to = to;
    pushPacket(outbox, packet);

    return true;
}